package com.lifeonwalden.game.mindustry;

import com.badlogic.gdx.*;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.utils.ScreenUtils;
import com.lifeonwalden.game.mindustry.constant.Toward;
import com.lifeonwalden.game.mindustry.entity.Bullet;
import com.lifeonwalden.game.mindustry.entity.Mech;
import com.lifeonwalden.game.mindustry.entity.Surface;
import com.lifeonwalden.game.mindustry.feature.Control;
import com.lifeonwalden.game.mindustry.model.BuildOperation;
import com.lifeonwalden.game.mindustry.processor.KeyPressedProcessor;
import com.lifeonwalden.game.mindustry.ui.OperationCenter;
import com.lifeonwalden.game.mindustry.world.Coordinate;
import com.lifeonwalden.game.mindustry.world.World;

import java.util.Optional;

public class Mindustry extends ApplicationAdapter implements Control {
    SpriteBatch batch;
    World world;
    OperationCenter operationCenter;
    BitmapFont font;
    OrthographicCamera camera;
    private int x = 512;
    private int y = 512;
    KeyPressedProcessor keyPressedProcessor;
    TextureAtlas textureAtlas;

    @Override
    public void create() {
        Gdx.graphics.setWindowedMode(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.graphics.setForegroundFPS(144);
        camera = new OrthographicCamera();
        batch = new SpriteBatch();
        font = new BitmapFont();

        textureAtlas = new TextureAtlas(Gdx.files.internal("sprites/mindustry.atlas"));
        Pixmap pixmap = new Pixmap(Gdx.files.internal("maps/map.png"));
        world = new World(pixmap, textureAtlas);
        pixmap.dispose();

        Global.world = world;

        operationCenter = new OperationCenter(textureAtlas, font);
        Gdx.input.setInputProcessor(new InputMultiplexer(operationCenter.stage, new InputAdapter() {

            @Override
            public boolean keyDown(int keycode) {
                switch (keycode) {
                    case Input.Keys.UP: {
                        y += Gdx.graphics.getHeight() / 100;
                        return true;
                    }
                    case Input.Keys.DOWN: {
                        y -= Gdx.graphics.getHeight() / 100;
                        return true;
                    }
                    case Input.Keys.G: {
                        Mech mech = new Mech();
                        Coordinate coordinate = world.projectedCoordinate(Gdx.input.getX(), Gdx.input.getY());
                        mech.x = coordinate.x;
                        mech.y = coordinate.y;

                        Global.world.build(mech);

                        return true;
                    }
                }

                return false;
            }

            @Override
            public boolean touchDown(int screenX, int screenY, int pointer, int button) {
                switch (button) {
                    case Input.Buttons.LEFT: {
                        if (null != OperationCenter.toBuildUnit) {
                            Global.buildOperation = new BuildOperation();
                            Global.buildOperation.buildQueue.add(OperationCenter.toBuildUnit);
                            Global.buildOperation.startUnit = OperationCenter.toBuildUnit;
                            Global.buildOperation.startScreenPoint = new Coordinate((short) screenX, (short) screenY);
                            OperationCenter.toBuildUnit = null;
                        } else {
                            Coordinate targetPoint = Global.world.projectedCoordinate(Gdx.input.getX(), Gdx.input.getY());
                            Optional<Bullet> bullet = Global.world.player.shoot(targetPoint.x, targetPoint.y);
                            if (bullet.isPresent()) {
                                Global.world.addBullet(bullet.get());
                            }
                        }
                        break;
                    }
                }
                return false;
            }

            @Override
            public boolean touchDragged(int screenX, int screenY, int pointer) {
                switch (pointer) {
                    case Input.Buttons.LEFT: {
                        if (null != Global.buildOperation) {
                            Coordinate point = world.projectedCoordinate(screenX, screenY);
                            int width = Global.buildOperation.startUnit.type.texture.getRegionWidth(), height = Global.buildOperation.startUnit.type.texture.getRegionHeight();
                            short startX = Global.buildOperation.startUnit.x, startY = Global.buildOperation.startUnit.y;
                            if (Math.abs(screenX - Global.buildOperation.startScreenPoint.x) < Math.abs(screenY - Global.buildOperation.startScreenPoint.y)) {
                                if (point.y > startY) {
                                    Global.buildOperation.startUnit.toward = Toward.UP;
                                    int availableHeight = point.y - startY;
                                    int count = availableHeight / height + (availableHeight % height != 0 ? 1 : 0);
                                    for (int i = 1; i < count; i++) {
                                        Surface surface;
                                        if (Global.buildOperation.buildQueue.size > i) {
                                            surface = Global.buildOperation.buildQueue.get(i);
                                        } else {
                                            surface = Global.buildOperation.startUnit.copy(false);
                                            Global.buildOperation.buildQueue.add(surface);
                                        }
                                        surface.y = (short) (startY + i * height);
                                    }

                                    if (Global.buildOperation.buildQueue.size > count) {
                                        Global.buildOperation.buildQueue.removeRange(count, Global.buildOperation.buildQueue.size - 1);
                                    }
                                } else {
                                    Global.buildOperation.startUnit.toward = Toward.BOTTOM;
                                    int availableHeight = startY - point.y;
                                    int count = availableHeight / height + (availableHeight % height != 0 ? 1 : 0) + 1;
                                    for (int i = 1; i < count; i++) {
                                        Surface surface;
                                        if (Global.buildOperation.buildQueue.size > i) {
                                            surface = Global.buildOperation.buildQueue.get(i);
                                        } else {
                                            surface = Global.buildOperation.startUnit.copy(false);
                                            Global.buildOperation.buildQueue.add(surface);
                                        }
                                        surface.y = (short) (startY - i * height);
                                    }

                                    if (Global.buildOperation.buildQueue.size > count) {
                                        Global.buildOperation.buildQueue.removeRange(count, Global.buildOperation.buildQueue.size - 1);
                                    }
                                }
                            } else {
                                if (point.x > startX) {
                                    Global.buildOperation.startUnit.toward = Toward.RIGHT;
                                    int availableWidth = point.x - startX;
                                    int count = availableWidth / width + (availableWidth % width != 0 ? 1 : 0);
                                    for (int i = 1; i < count; i++) {
                                        Surface surface;
                                        if (Global.buildOperation.buildQueue.size > i) {
                                            surface = Global.buildOperation.buildQueue.get(i);
                                        } else {
                                            surface = Global.buildOperation.startUnit.copy(false);
                                            Global.buildOperation.buildQueue.add(surface);
                                        }
                                        surface.x = (short) (startX + i * width);
                                    }

                                    if (Global.buildOperation.buildQueue.size > count) {
                                        Global.buildOperation.buildQueue.removeRange(count, Global.buildOperation.buildQueue.size - 1);
                                    }
                                } else {
                                    Global.buildOperation.startUnit.toward = Toward.LEFT;
                                    int availableWidth = startX - point.x;
                                    int count = availableWidth / width + (availableWidth % width != 0 ? 1 : 0) + 1;
                                    for (int i = 1; i < count; i++) {
                                        Surface surface;
                                        if (Global.buildOperation.buildQueue.size > i) {
                                            surface = Global.buildOperation.buildQueue.get(i);
                                        } else {
                                            surface = Global.buildOperation.startUnit.copy(false);
                                            Global.buildOperation.buildQueue.add(surface);
                                        }
                                        surface.x = (short) (startX - i * width);
                                    }

                                    if (Global.buildOperation.buildQueue.size > count) {
                                        Global.buildOperation.buildQueue.removeRange(count, Global.buildOperation.buildQueue.size - 1);
                                    }
                                }
                            }
                        }
                        break;
                    }
                }

                return false;
            }

            @Override
            public boolean touchUp(int screenX, int screenY, int pointer, int button) {
                switch (button) {
                    case Input.Buttons.LEFT: {
                        if (null != Global.buildOperation) {
                            for (Surface surface : Global.buildOperation.buildQueue) {
                                if (!world.build(surface)) {
                                    break;
                                }
                            }

                            Global.buildOperation = null;
                            return true;
                        }
                        break;
                    }
                    case Input.Buttons.RIGHT: {
                        if (null != OperationCenter.toBuildUnit) {
                            OperationCenter.toBuildUnit = null;

                            return true;
                        } else if (null != Global.buildOperation) {
                            Global.buildOperation = null;
                            return true;
                        } else {
                            world.removeUnit();

                            return true;
                        }
                    }
                }

                return false;
            }

            @Override
            public boolean scrolled(float amountX, float amountY) {
                if (amountY > 0) {
                    world.zoomOut();
                } else {
                    world.zoomIn();
                }
                camera.setToOrtho(false, (float) (Gdx.graphics.getWidth() * world.scaling), (float) (Gdx.graphics.getHeight() * world.scaling));

                return true;
            }
        }));

        keyPressedProcessor = new KeyPressedProcessor(this);
        camera.setToOrtho(false, (float) (Gdx.graphics.getWidth() * world.scaling), (float) (Gdx.graphics.getHeight() * world.scaling));
    }

    @Override
    public void render() {
        ScreenUtils.clear(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        keyPressedProcessor.process();
        // tell the camera to update its matrices.
        camera.position.set(world.player.x, world.player.y, 0);
        camera.update();

        batch.setProjectionMatrix(camera.combined);

        world.doJobBefore();

        batch.begin();
        world.draw(batch);
        font.draw(batch, "Memory : ".concat(String.valueOf(Gdx.app.getJavaHeap() / (1024 * 1024))).concat(" Native: ").concat(String.valueOf(Gdx.app.getNativeHeap() / (1024 * 1024))), 100, 400);
        font.draw(batch, "FPS : ".concat(String.valueOf(Gdx.graphics.getFramesPerSecond())), 100, 100);
        font.draw(batch, "Size : ".concat(String.valueOf(Gdx.graphics.getHeight())).concat(" ").concat(String.valueOf(Gdx.graphics.getWidth())), 100, 200);
        font.draw(batch, "Position : ".concat(String.valueOf(x)).concat(" ").concat(String.valueOf(y)), 100, 300);
        if (null != OperationCenter.toBuildUnit) {
            Coordinate coordinate = world.projectedCoordinate(Gdx.input.getX(), Gdx.input.getY());

            OperationCenter.toBuildUnit.x = (short) (coordinate.x - OperationCenter.toBuildUnit.texture().getRegionWidth() / 2);
            OperationCenter.toBuildUnit.y = (short) (coordinate.y - OperationCenter.toBuildUnit.texture().getRegionHeight() / 2);
            batch.draw(OperationCenter.toBuildUnit.texture(), OperationCenter.toBuildUnit.x, OperationCenter.toBuildUnit.y);
        } else if (null != Global.buildOperation) {
            for (Surface surface : Global.buildOperation.buildQueue) {
                batch.draw(surface.texture(), surface.x, surface.y);
            }
        }
        batch.end();

        world.doJobAfter();

        operationCenter.countingBoard.stoneCounting.setText(world.resourceWarehouse.stoneCount);
        operationCenter.stage.act(Gdx.input.getDeltaX());
        operationCenter.stage.draw();
    }

    @Override
    public void dispose() {
        batch.dispose();
        world.dispose();
        operationCenter.dispose();
        font.dispose();
        textureAtlas.dispose();
    }

    @Override
    public OrthographicCamera camera() {
        return this.camera;
    }

    @Override
    public World world() {
        return this.world;
    }

    @Override
    public void resize(int width, int height) {
        camera.setToOrtho(false, (float) (width * world.scaling), (float) (height * world.scaling));
        camera.update();

        operationCenter.resize(width, height);
    }
}
